package cn.iocoder.yudao.module.service.framework.security.core.filter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.service.controller.admin.customer.vo.ServiceCustomerSaveReqVO;
import cn.iocoder.yudao.module.service.dal.dataobject.assistant.ServiceAssistantDO;
import cn.iocoder.yudao.module.service.dal.dataobject.customer.ServiceCustomerDO;
import cn.iocoder.yudao.module.service.service.customer.ServiceCustomerService;
import cn.iocoder.yudao.module.service.utils.BuildWSUserIdUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getUserAgent;

/**
 * Token 过滤器，验证 token 的有效性
 * 验证通过后，获得 {@link LoginUser} 信息，并加入到 Spring Security 上下文
 *
 * @author 芋道源码
 */
@RequiredArgsConstructor
@Slf4j
@Component
public class ServiceTokenAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private ServiceCustomerService serviceCustomerService;


    private static final String IDF = "service";

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        String userType = request.getParameter("userType");
        String scene = request.getParameter("scene");
        if (!Objects.equals(IDF, scene)) {
            chain.doFilter(request, response);
        } else {
            if (StrUtil.isEmpty(userType)) {
                log.error("User Type Empty");
                return;
            }
            // 1 是会员 2 是管理员
            UserTypeEnum userTypeEnum = UserTypeEnum.valueOf(Integer.parseInt(userType));
            if (Objects.equals(userTypeEnum, UserTypeEnum.ADMIN)) {
                // 管理员使用token验证，直接放过
                chain.doFilter(request, response);
            }
            else if (Objects.equals(userTypeEnum, UserTypeEnum.MEMBER)) {
                String tenantId = request.getParameter("tenantId");
                if (StrUtil.isEmpty(tenantId)) {
                    log.error("Tenant is null ");
                    return;
                }
                TenantUtils.execute(Long.parseLong(tenantId), () -> {
                    // 客服token
                    String customerSessionId = request.getParameter("sessionId");
                    String clientType = request.getParameter("clientType");
                    String assistantId = request.getParameter("assistantId");
                    if (StrUtil.isEmpty(customerSessionId)) {
                        log.error("Lost params [sessionId]");
                        return;
                    }
                    if (StrUtil.isEmpty(clientType)) {
                        log.error("Lost params [clientType]");
                        return;
                    }
                    ServiceCustomerDO customer = serviceCustomerService.getCustomer(customerSessionId);
                    if (Objects.isNull(customer)) {
                        // 新的会话
                        customer = new ServiceCustomerDO()

                                .setSessionId(customerSessionId)
                                .setTenantId(Long.parseLong(tenantId))
                                .setAssistantId(Long.parseLong(assistantId))
                                .setName("昵称：" + DateUtil.currentSeconds())
                                .setIp(getClientIP())
                                .setUserAgent(getUserAgent())
                                .setClientType(Integer.parseInt(clientType));
                        customer.setId(serviceCustomerService.createCustomer(BeanUtils.toBean(customer, ServiceCustomerSaveReqVO.class)));

                        customer.setLoginUserId(BuildWSUserIdUtil.buildCustomerUserId(customer.getId()));
                        serviceCustomerService.updateCustomer(BeanUtils.toBean(customer, ServiceCustomerSaveReqVO.class));
                        log.info("Create new User : {}", JSONUtil.toJsonStr(customer));
                    }
                    LoginUser loginUser = buildUserFromCustomer(customer);
                    log.info("Set LoginUser : {}", JSONUtil.toJsonStr(loginUser));

                    SecurityFrameworkUtils.setLoginUser(loginUser, request);

                });
            } else {
                log.error("Client Type Not Found");
            }
            chain.doFilter(request, response);
        }
    }

    private LoginUser buildUserFromCustomer(ServiceCustomerDO customer) {
        LoginUser loginUser = new LoginUser();
        loginUser.setId(customer.getLoginUserId());
        loginUser.setUserType(UserTypeEnum.MEMBER.getValue());
        loginUser.setTenantId(customer.getTenantId());
        loginUser.setSessionId(customer.getSessionId());
        return loginUser;
    }

    private LoginUser buildUserFromAssistant(ServiceAssistantDO assistant) {
        LoginUser loginUser = new LoginUser();
        loginUser.setId(BuildWSUserIdUtil.buildAssistantUserId(assistant.getId()));
        loginUser.setUserType(UserTypeEnum.ADMIN.getValue());
        loginUser.setTenantId(assistant.getTenantId());

        return loginUser;
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return !StrUtil.equals(request.getRequestURI(), "/infra/ws");
    }
}
